package db

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/samber/do/v2"
	"github.com/spf13/viper"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// Init 初始化数据库连接
func Init(i do.Injector) (*gorm.DB, error) {
	// 创建GORM配置
	gormConfig := &gorm.Config{}

	// 设置日志级别
	logLevel := viper.GetString("db.log_level")
	switch logLevel {
	case "silent":
		gormConfig.Logger = logger.Default.LogMode(logger.Silent)
	case "error":
		gormConfig.Logger = logger.Default.LogMode(logger.Error)
	case "warn":
		gormConfig.Logger = logger.Default.LogMode(logger.Warn)
	case "info":
		gormConfig.Logger = logger.Default.LogMode(logger.Info)
	default:
		gormConfig.Logger = logger.Default.LogMode(logger.Info)
	}

	// 根据驱动类型创建不同的连接
	var dialector gorm.Dialector

	driver := viper.GetString("db.driver")
	dsn := viper.GetString("db.dsn")
	switch driver {
	case "mysql":
		dialector = mysql.Open(dsn)
	case "postgres":
		dialector = postgres.Open(dsn)
	case "sqlite":
		dialector = sqlite.Open(dsn)
	default:
		return nil, fmt.Errorf("unsupported database driver: %s", driver)
	}

	// 连接数据库
	db, err := gorm.Open(dialector, gormConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to connect database: %w", err)
	}

	// 获取底层SQL连接，配置连接池
	sqlDB, err := db.DB()
	if err != nil {
		return nil, fmt.Errorf("failed to get underlying database: %w", err)
	}

	// 配置连接池
	maxOpenConns := viper.GetInt("db.max_open_conns")
	maxIdleConns := viper.GetInt("db.max_idle_conns")
	connMaxLifetime := viper.GetDuration("db.conn_max_lifetime")
	sqlDB.SetMaxOpenConns(maxOpenConns)
	sqlDB.SetMaxIdleConns(maxIdleConns)
	sqlDB.SetConnMaxLifetime(connMaxLifetime)

	// 测试连接
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := sqlDB.PingContext(ctx); err != nil {
		return nil, fmt.Errorf("failed to ping database: %w", err)
	}

	log.Printf("database connected: %s", driver)
	return db, nil
}

// CloseDB 关闭数据库连接
// CloseDB 应该在关闭的时候，也关闭db
func CloseDB(db *gorm.DB) error {
	if db == nil {
		return nil
	}

	sqlDB, err := db.DB()
	if err != nil {
		return fmt.Errorf("failed to get underlying database: %w", err)
	}

	return sqlDB.Close()
}
